#include "bsp.h"
#include "systick.h"
#include "main.h"
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "stdarg.h"
#include "log.h"

static void LED_Init(void);
static void timer_config(void);
static void USART0_init(void);
static void WatchDog_init(void);
static void ReadWriteProtection(void);

void bsp_init(void)
{
   // ReadWriteProtection();
  	NVIC_SetPriorityGrouping(3);//NVIC_PRIORITYGROUP_4      ((uint32_t)0x00000003) /*!< 4 bits for pre-emption priority	0 bits for subpriority * /
    systick_config();
    LED_Init();
    rcu_periph_clock_enable(RCU_CRC);
    timer_config();
    USART0_init();
}

static void ReadWriteProtection(void)
{
    uint32_t wp_value = 0xFFFFFFFF, protected_pages = 0x0;
    __IO fmc_state_enum fmc_state = FMC_READY;
       /* unlock the flash program/erase controller */
    fmc_unlock();
    ob_unlock();

    fmc_flag_clear(FMC_FLAG_BANK0_END);
    fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
    fmc_flag_clear(FMC_FLAG_BANK0_PGERR);

    /* get pages write protection status */
    wp_value = ob_write_protection_get();

#ifdef WRITE_PROTECTION_DISABLE
    /* get pages already write protected */
    protected_pages = ~(wp_value | FMC_PAGES_PROTECTED);
    /* check if desired pages are already write protected */
    if((wp_value | (~FMC_PAGES_PROTECTED)) != 0xFFFFFFFF ){
        /* erase all the option Bytes */
        fmc_state = ob_erase();
    
        /* check if there is write protected pages */
        if(protected_pages != 0x0){
            /* Restore write protected pages */
            fmc_state = ob_write_protection_enable(protected_pages);   
        }
        if(SET == ob_spc_get())
        fmc_state = ob_security_protection_config(0xA5); //disable read protection
        /* generate system reset to load the new option byte values */
        fmc_lock();
        ob_lock();
        NVIC_SystemReset();
    }
#elif defined WRITE_PROTECTION_ENABLE
    /* get current write protected pages and the new pages to be protected */
    protected_pages = (~wp_value) | FMC_PAGES_PROTECTED; 
  
    /* check if desired pages are not yet write protected */
    if(((~wp_value) & FMC_PAGES_PROTECTED )!= FMC_PAGES_PROTECTED){
  
        /* erase all the option bytes because if a program operation is 
        performed on a protected page, the flash memory returns a 
        protection error */
        fmc_state = ob_erase();

        /* enable the pages write protection */
        fmc_state = ob_write_protection_enable(protected_pages);   //protect the bootloader flash area

        if(RESET == ob_spc_get())
        fmc_state = ob_security_protection_config(0x00);     //enable read protection
        /* generate system reset to load the new option byte values */
        fmc_lock();
        ob_lock();
        NVIC_SystemReset();
    }
#endif /* WRITE_PROTECTION_DISABLE */
}


static void LED_Init(void)
{
        /* enable the led clock */
    rcu_periph_clock_enable(LED1_GPIO_CLK);
    /* configure led GPIO port */ 
    gpio_init(LED1_GPIO_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,LED1_PIN);
}

 void Freedog(void)
{
	fwdgt_counter_reload();
}
   
/*!
    \brief      toggle the led every 500ms
    \param[in]  none
    \param[out] none
    \retval     none
*/
void led_spark(void)
{
    static __IO uint8_t timingdelaylocal = 0;


        if(timingdelaylocal){
           LED1_ON();
        }else{
           LED1_OFF();
        }

        timingdelaylocal++;
      if(timingdelaylocal>1)timingdelaylocal = 0;
}


/**
    \brief      configure the TIMER peripheral
    \param[in]  none
    \param[out] none
    \retval     none
  */
  void timer_config(void)
  {
      /* -----------------------------------------------------------------------
      TIMER6 configuration:
      generate 3 complementary PWM signal.
      TIMER6CLK is fixed to systemcoreclock, the TIMER6 prescaler is equal to 120 
      so the TIMER6 counter clock used is 1MHz.
      insert a dead time equal to 200/systemcoreclock =1.67us 
      configure the break feature, active at low level, and using the automatic
      output enable feature.
      use the locking parameters level 0.
      ----------------------------------------------------------------------- */


      timer_parameter_struct timer_initpara;

      rcu_periph_clock_enable(RCU_TIMER6);
  
      timer_deinit(TIMER6);
  
      /* TIMER6 configuration */
      timer_initpara.prescaler         = 10-1;
      timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
      timer_initpara.counterdirection  = TIMER_COUNTER_UP;
      timer_initpara.period            = 0xFFFF;
      timer_initpara.clockdivision     = TIMER_CKDIV_DIV2;
      timer_initpara.repetitioncounter = 0;
      timer_init(TIMER6,&timer_initpara);

      /* TIMER6 counter enable */
      timer_enable(TIMER6);
  }

void delay_us(uint32_t us)
{
    TIMER_CNT(TIMER6) = 0;
    while(us*12 >TIMER_CNT(TIMER6)); 
}

void delay_ms(uint32_t ms)
{
    while(ms--)
    {
        delay_us(1000);
    }
}

/*!
    \brief      configure COM port
    \param[in]  com: COM on the board
      \arg        EVAL_COM1: COM1 on the board
    \param[out] none
    \retval     none
*/
void USART0_init(void)
{  
    /* enable GPIO clock */
    rcu_periph_clock_enable(USART0_TX_GPIO_CLK);
	rcu_periph_clock_enable(USART0_RX_GPIO_CLK);
    /* enable USART clock */
    rcu_periph_clock_enable(USART0_CLK);

    /* connect port to USARTx_Tx */
    gpio_init(USART0_TX_GPIO_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, USART0_TX_PIN);
    /* connect port to USARTx_Rx */
    gpio_init(USART0_RX_GPIO_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, USART0_RX_PIN);

    /* USART configure */
    usart_deinit(USART0);
    usart_baudrate_set(USART0, 115200U);
    usart_receive_config(USART0, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
    usart_enable(USART0);
}



/* USER CODE BEGIN 4 */
void UART_Send_Buf(uint32_t uartx,uint8_t *buf, uint16_t size)
{
	for (uint16_t i = 0; i < size; i++)
	{
		while (!usart_flag_get(uartx, USART_FLAG_TBE) && !usart_flag_get(uartx, USART_FLAG_TC));
		usart_data_transmit(uartx,buf[i]);
	}
}


void UART_Printf(uint32_t uartx, const char* fmt, ...)
{
	uint16_t len;
	char printfbuf[512];
	memset(printfbuf, 0, 512);
	va_list ap;
	va_start(ap, fmt);
	len = vsprintf(printfbuf, fmt, ap);
	va_end(ap);
	UART_Send_Buf(uartx, (uint8_t*)printfbuf, len);
}


static void WatchDog_init(void)
{
    /* confiure FWDGT counter clock: 40KHz(IRC40K) / 64 = 0.625 KHz */
    fwdgt_config(500,FWDGT_PSC_DIV64);     //500 * 1/ 625  = 0.8s 
    
    /* After 1.6 seconds to generate a reset */
    fwdgt_enable();
	
	    /* check if the system has resumed from FWDGT reset */
    if (RESET != rcu_flag_get(RCU_FLAG_FWDGTRST)){

        rcu_all_reset_flag_clear();
        log_info("Reset by WatchDOg");      
    }
}

void WatchDog_Disable(void)
{
	FWDGT_CTL &= ~FWDGT_KEY_ENABLE;
}


/**
* @brief  Main program.
* @param  None
* @retval None
*/
static fmc_state_enum FLASHStatus = FMC_READY;

uint8_t writeToFlash(char* databuf, uint32_t writelength, uint32_t targetflashaddress)
{
	uint32_t NbrOfPage = 0x00;
	uint32_t Address = 0x00;
	/* first check the write address */
	if (targetflashaddress % 4 != 0)
	{
		targetflashaddress = (targetflashaddress / 4 + 1) * 4;
	}

	if (targetflashaddress < CRC_FLASH_ADDR || targetflashaddress > FLASH_APP1_END_ADDR)
	{	//write failure	for the invalid address
		log_err("data to write is out of the user flash!");
		return 1;
	}

	uint32_t targetflashEndaddress = targetflashaddress + writelength;

	/* Unlock the Flash *********************************************************/
	/* Enable the flash control register access */
	fmc_unlock();

	/* Erase the user Flash area ************************************************/
	/* area defined by FLASH_USER_START_ADDR and FLASH_USER_END_ADDR */

	/* Clear pending flags (if any) */
	fmc_flag_clear(FMC_FLAG_BANK0_PGERR | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_END);


	/* Define the number of page to be erased */
	NbrOfPage = (targetflashEndaddress - targetflashaddress) / FLASH_PAGE_SIZE;
	NbrOfPage++;

	// /* Erase the FLASH pages */
	// for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FMC_READY); EraseCounter++)
	// {
	// 	if (fmc_page_erase(targetflashaddress + (FLASH_PAGE_SIZE * EraseCounter)) != FMC_READY)
	// 	{
	// 		/* Error occurred while sector erase.
	// 		User can add here some code to deal with this error  */
	// 		while (1)
	// 		{
	// 		}
	// 	}
	// }

	Address = targetflashaddress;
	uint32_t* towrite = (uint32_t *)databuf;
	while (Address < targetflashEndaddress)
	{
		if (fmc_word_program(Address, *towrite) == FMC_READY)
		{
			Address = Address + 4;
			towrite++;
		}
		else
		{
			/* Error occurred while writing data in Flash memory.
			User can add here some code to deal with this error */
			while (1)
			{
			}
		}

	}

	/* Lock the Flash to disable the flash control register access (recommended
	to protect the FLASH memory against possible unwanted operation) */
	fmc_lock();
	return 1;
}

uint8_t erase_flash( uint32_t writelength, uint32_t targetflashaddress)
{

    uint32_t NbrOfPage = 0x00;
	uint32_t EraseCounter = 0x00;
	/* first check the write address */
	if (targetflashaddress % 4 != 0)
	{
		targetflashaddress = (targetflashaddress / 4 + 1) * 4;
	}

	if (targetflashaddress < CRC_FLASH_ADDR || targetflashaddress > FLASH_APP1_END_ADDR)
	{	//write failure	for the invalid address
		log_err("data to write is out of the user flash!");
		return 1;
	}

	uint32_t targetflashEndaddress = targetflashaddress + writelength;

	/* Unlock the Flash *********************************************************/
	/* Enable the flash control register access */
	fmc_unlock();

	/* Erase the user Flash area ************************************************/
	/* area defined by FLASH_USER_START_ADDR and FLASH_USER_END_ADDR */

	/* Clear pending flags (if any) */
	fmc_flag_clear(FMC_FLAG_BANK0_PGERR | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_END);


	/* Define the number of page to be erased */
	NbrOfPage = (targetflashEndaddress - targetflashaddress) / FLASH_PAGE_SIZE;
	NbrOfPage++;

	/* Erase the FLASH pages */
	for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FMC_READY); EraseCounter++)
	{
		if (fmc_page_erase(targetflashaddress + (FLASH_PAGE_SIZE * EraseCounter)) != FMC_READY)
		{
			/* Error occurred while sector erase.
			User can add here some code to deal with this error  */
			while (1)
			{
			}
		}
	}
return 1;
}
